package entry.custom.filter;

import java.net.URI;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;

import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequest.Builder;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.util.UriComponentsBuilder;

import entry.custom.predicate.SelfPredicateRoutePredicateFactory.Config;
import reactor.core.publisher.Mono;

/**
 * 自定义Filter过滤器
 * 注意事项：
 * 	1、自定义过滤器工厂必须是Spring组件，即IOC容器中的bean。
 * 	2、自定义过滤器工厂必须继承自抽象类AbstractGatewayFilterFactory。
 * 	   2.1、AbstractGatewayFilterFactory还有其他的抽象子类可以继承使用，本Demo采用顶级父类来实现功能
 * 	3、自定义过滤器工厂命名必须是：xxxxGatewayFilterFactory，即以'GatewayFilterFactory'结尾。
 * 	   使用时，Gateway底层会根据'xxxx' + 'GatewayFilterFactory'，来进行反射查找对应的过滤器工厂。
 * 	4、自定义过滤器工厂类中必须实现一个静态内部类 -> public static class Config{ xxx xxx }
 * 	   Config类的作用是用来接收yml配置文件中的配置信息。
 *     4.1、经过测试，该项并非必须项，也可以使用外部定义bean来完成Config。
 * 	5、自定义断言工厂类中必须实现shortcutFieldOrder()方法，来完成yml配置与Config类之间的值绑定。
 */
@Component
public class SelfFilterNoInnerGatewayFilterFactory extends AbstractGatewayFilterFactory<SelfFilterNoInnerConfig> {
	
	// 构造函数需要指定Config类进行传入
	public SelfFilterNoInnerGatewayFilterFactory() {
		super(SelfFilterNoInnerConfig.class);
	}
	
	@Override
	public GatewayFilter apply(SelfFilterNoInnerConfig config) {
		// 过滤器功能实现
		return new GatewayFilter() {
			@Override
			public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
				// 通过该方法获取当次这次的Request请求对象
				ServerHttpRequest req = exchange.getRequest();
				// 通过该方法获取当次这次的Response响应对象
				ServerHttpResponse resp = exchange.getResponse();
				// 获取请求builder生成器，通过该builder来修改请求中的属性值
				Builder builder = req.mutate();
				builder.headers(new Consumer<HttpHeaders>() {
					@Override
					public void accept(HttpHeaders t) {
						t.add(config.getHeaderK(), config.getHeaderV());
					}
				});
				ServerHttpRequest newRequest = builder.build();
				
				URI newUri = UriComponentsBuilder.fromUri(req.getURI())
						.replaceQuery(config.getParamK()+"="+config.getParamV()).build(true).toUri();
				ServerHttpRequest newRequestWithParam = newRequest.mutate().uri(newUri).build();
				
				return chain.filter(exchange.mutate().request(newRequestWithParam).build());
			}
		};
	}
	
	/**
	 * 通过该方法向Config类进行“值”设置
	 * 传入的值即为Config中的属性名，通过查找Config中的set方法进行设置“值”
	 * TIPS:
	 * 	如果yml配置中采用的是filters.name、filters.args.属性a/b/c...，的方式配置，shortcutFieldOrder()方法可以不要。
	 */
//	@Override
//	public List<String> shortcutFieldOrder() {
//		return Arrays.asList("headerK","headerV","paramK","paramV");
//	}
}
